/*-------------------------------------------------------------------------
 *
 * proclist.h
 *		对 pgprocnos 的双向链表操作
 *
 * 接口类似于 ilist.h 中的 dlist，但使用 pgprocno 而不是指针。这允许 proclist_head 在不同的后端映射到不同的地址。
 *
 * 请参见 proclist_types.h，了解这些函数操作的结构体。它们被分开以打破与 proc.h 的头文件依赖循环。
 *
 * Portions Copyright (c) 2016-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *		src/include/storage/proclist.h
 *-------------------------------------------------------------------------
 */
#ifndef PROCLIST_H
#define PROCLIST_H

#include "storage/proc.h"
#include "storage/proclist_types.h"

/*
 * 初始化一个proclist。
 */
static inline void
proclist_init(proclist_head *list)
{
	list->head = list->tail = INVALID_PGPROCNO;
}

/*
 * 列表为空吗？
 */
static inline bool
proclist_is_empty(proclist_head *list)
{
	return list->head == INVALID_PGPROCNO;
}

/*
 * 根据procno和PGPROC中proclist_node字段的偏移量，
 * 获取指向给定PGPROC内的proclist_node的指针。
 */
static inline proclist_node *
proclist_node_get(int procno, size_t node_offset)
{
	char	   *entry = (char *) GetPGProcByNumber(procno);

	return (proclist_node *) (entry + node_offset);
}

/*
 * 在列表的开头插入一个进程。
 */
static inline void
proclist_push_head_offset(proclist_head *list, int procno, size_t node_offset)
{
	proclist_node *node = proclist_node_get(procno, node_offset);

	Assert(node->next == 0 && node->prev == 0);

	if (list->head == INVALID_PGPROCNO)
	{
		Assert(list->tail == INVALID_PGPROCNO);
		node->next = node->prev = INVALID_PGPROCNO;
		list->head = list->tail = procno;
	}
	else
	{
		Assert(list->tail != INVALID_PGPROCNO);
		Assert(list->head != procno);
		Assert(list->tail != procno);
		node->next = list->head;
		proclist_node_get(node->next, node_offset)->prev = procno;
		node->prev = INVALID_PGPROCNO;
		list->head = procno;
	}
}

/*
 * 在列表的末尾插入一个进程。
 */
static inline void
proclist_push_tail_offset(proclist_head *list, int procno, size_t node_offset)
{
	proclist_node *node = proclist_node_get(procno, node_offset);

	Assert(node->next == 0 && node->prev == 0);

	if (list->tail == INVALID_PGPROCNO)
	{
		Assert(list->head == INVALID_PGPROCNO);
		node->next = node->prev = INVALID_PGPROCNO;
		list->head = list->tail = procno;
	}
	else
	{
		Assert(list->head != INVALID_PGPROCNO);
		Assert(list->head != procno);
		Assert(list->tail != procno);
		node->prev = list->tail;
		proclist_node_get(node->prev, node_offset)->next = procno;
		node->next = INVALID_PGPROCNO;
		list->tail = procno;
	}
}

/*
 * 从列表中删除一个进程——它必须在列表中！
 */
static inline void
proclist_delete_offset(proclist_head *list, int procno, size_t node_offset)
{
	proclist_node *node = proclist_node_get(procno, node_offset);

	Assert(node->next != 0 || node->prev != 0);

	if (node->prev == INVALID_PGPROCNO)
	{
		Assert(list->head == procno);
		list->head = node->next;
	}
	else
		proclist_node_get(node->prev, node_offset)->next = node->next;

	if (node->next == INVALID_PGPROCNO)
	{
		Assert(list->tail == procno);
		list->tail = node->prev;
	}
	else
		proclist_node_get(node->next, node_offset)->prev = node->prev;

	node->next = node->prev = 0;
}

/*
 * 检查一个进程当前是否在列表中。必须知道该
 * 进程不在使用相同proclist_node的任何_其他_ proclist中，
 * 所以唯一的可能性是它在这个列表中或不在。
 */
static inline bool
proclist_contains_offset(proclist_head *list, int procno,
						 size_t node_offset)
{
	proclist_node *node = proclist_node_get(procno, node_offset);

	/* 如果它不在任何列表中，它肯定不在这个列表中。 */
	if (node->prev == 0 && node->next == 0)
		return false;

	/*
	 * 实际上，它必须在该列表中。理想情况下，在启用断言的构建中，
	 * 我们将验证这一点。但是，由于此函数通常在保持自旋锁时使用，
	 * 遍历整个列表是不可接受的。然而，当节点是列表头或
	 * 尾时，我们可以在 O(1) 时间内验证该问题，这似乎是值得的，
	 * 因为在实践中，这通常应该足以捕捉错误。
	 */
	Assert(node->prev != INVALID_PGPROCNO || list->head == procno);
	Assert(node->next != INVALID_PGPROCNO || list->tail == procno);

	return true;
}

/*
 * 删除并返回列表中的第一个进程（必须有一个）。
 */
static inline PGPROC *
proclist_pop_head_node_offset(proclist_head *list, size_t node_offset)
{
	PGPROC	   *proc;

	Assert(!proclist_is_empty(list));
	proc = GetPGProcByNumber(list->head);
	proclist_delete_offset(list, list->head, node_offset);
	return proc;
}

/*
 * 辅助宏以避免重复offsetof(PGPROC, <member>)。
 * 'link_member'是PGPROC中proclist_node成员的名称。
 */
#define proclist_delete(list, procno, link_member) \
	proclist_delete_offset((list), (procno), offsetof(PGPROC, link_member))
#define proclist_push_head(list, procno, link_member) \
	proclist_push_head_offset((list), (procno), offsetof(PGPROC, link_member))
#define proclist_push_tail(list, procno, link_member) \
	proclist_push_tail_offset((list), (procno), offsetof(PGPROC, link_member))
#define proclist_pop_head_node(list, link_member) \
	proclist_pop_head_node_offset((list), offsetof(PGPROC, link_member))
#define proclist_contains(list, procno, link_member) \
	proclist_contains_offset((list), (procno), offsetof(PGPROC, link_member))

/*
 * 遍历指向'lhead'的列表，将当前
 * 位置存储在'iter'中。'link_member'是PGPROC中proclist_node成员的名称。
 * 使用iter.cur访问当前位。
 *
 * 在迭代时唯一允许的列表修改是使用proclist_delete(list, iter.cur, node_offset)
 * 删除当前节点。
 */
#define proclist_foreach_modify(iter, lhead, link_member)					\
	for (AssertVariableIsOfTypeMacro(iter, proclist_mutable_iter),			\
		 AssertVariableIsOfTypeMacro(lhead, proclist_head *),				\
		 (iter).cur = (lhead)->head,										\
		 (iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO :	\
			 proclist_node_get((iter).cur,									\
							   offsetof(PGPROC, link_member))->next;		\
		 (iter).cur != INVALID_PGPROCNO;									\
		 (iter).cur = (iter).next,											\
		 (iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO :	\
			 proclist_node_get((iter).cur,									\
							   offsetof(PGPROC, link_member))->next)

#endif							/* PROCLIST_H */
